<template>
  <div class="min-h-screen bg-gray-50 p-5 max-w-6xl mx-auto">
    <div class="mb-6">
      <div class="bg-white rounded-xl shadow p-5">
        <div class="flex items-center justify-between">
          <h2 class="text-xl font-semibold text-gray-800">图像标注示例（矢量多边形）</h2>
          <div class="flex items-center gap-3">
            <button class="px-3 py-1 rounded bg-green-600 text-white hover:bg-green-700" @click="startPolygon" :disabled="drawingPoly">开始标注</button>
            <button class="px-3 py-1 rounded bg-blue-600 text-white hover:bg-blue-700" @click="finishPolygon" :disabled="!drawingPoly || currentPoints.length < 3">完成多边形</button>
            <button class="px-3 py-1 rounded bg-yellow-500 text-white hover:bg-yellow-600" @click="undoPoint" :disabled="!drawingPoly || currentPoints.length === 0">撤销节点</button>
            <button class="px-3 py-1 rounded bg-red-500 text-white hover:bg-red-600" @click="clearAnnotations">清除标注</button>
          </div>
        </div>
        <p class="mt-2 text-sm text-gray-600">点击图片区域以添加节点，完成后计算标注区域与未标注区域的面积比值。</p>
      </div>
    </div>

    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
      <div class="lg:col-span-2 bg-white rounded-xl shadow p-5">
        <div class="mb-4 flex items-center gap-3">
          <input type="file" accept="image/*" @change="onFileChange" class="block" />
          <button class="px-3 py-1 rounded bg-blue-600 text-white hover:bg-blue-700" @click="loadSample">加载示例图片</button>
        </div>
        <!-- 关键功能：画布区域，底层绘制图片，顶层 SVG 绘制矢量标注 -->
        <div class="relative bg-gray-100 rounded-lg overflow-hidden select-none">
          <canvas ref="baseCanvas" :width="canvasWidth" :height="canvasHeight" class="block"></canvas>
          <svg ref="svgOverlay" :width="canvasWidth" :height="canvasHeight" class="absolute inset-0 cursor-crosshair" @click="onSvgClick">
            <!-- 已完成的多边形标注 -->
            <polygon v-for="(poly, idx) in polygons" :key="'poly-'+idx" :points="poly.points.map(p => p.x + ',' + p.y).join(' ')" fill="rgba(239,68,68,0.35)" stroke="#ef4444" stroke-width="2" />
            <!-- 当前编辑中的多边形 -->
            <polyline v-if="drawingPoly && currentPoints.length > 0" :points="currentPoints.map(p => p.x + ',' + p.y).join(' ')" fill="none" stroke="#3b82f6" stroke-width="2" />
            <circle v-for="(p,i) in currentPoints" :key="'pt-'+i" :cx="p.x" :cy="p.y" r="3" fill="#3b82f6" />
          </svg>
        </div>
      </div>

      <div class="bg-white rounded-xl shadow p-5">
        <div class="text-gray-800 font-semibold mb-2">面积比值</div>
        <div class="space-y-2 text-sm text-gray-700">
          <div>标注区域面积（像素）：<span class="font-medium">{{ annotatedArea }}</span></div>
          <div>总面积（图片显示区域像素）：<span class="font-medium">{{ totalArea }}</span></div>
          <div>标注/未标注比值：<span class="font-medium">{{ ratioText }}</span></div>
        </div>
        <p class="mt-3 text-xs text-gray-500">提示：点击图片范围内添加节点，完成多边形后自动计算面积；清除标注会移除所有多边形。</p>
      </div>
    </div>

    <!-- 关键功能：页面级 Snackbar 组件实例 -->
    <Snackbar v-model:show="snackShow" :type="snackType" position="top" :duration="2000" :content="snackContent" />
  </div>
</template>

<script>
import Snackbar from '@/components/snackbar.vue'

export default {
  name: 'CanvasAnnotate',
  components: { Snackbar },
  data() {
    return {
      // 关键功能：画布尺寸与上下文
      canvasWidth: 960,
      canvasHeight: 540,
      baseCtx: null,
      // 关键功能：图片加载状态与对象
      image: null,
      imageLoaded: false,
      // 关键功能：图片显示矩形（用于限制标注范围与总面积）
      imageRect: { x: 0, y: 0, w: 0, h: 0 },
      // 关键功能：矢量标注状态
      polygons: [],
      currentPoints: [],
      drawingPoly: false,
      // 关键功能：面积计算状态
      annotatedArea: 0,
      totalArea: 0,
      // 关键功能：页面级提示
      snackShow: false,
      snackType: 'error',
      snackContent: ''
    }
  },
  computed: {
    ratioText() {
      const annotated = this.annotatedArea
      const total = this.totalArea || (this.canvasWidth * this.canvasHeight)
      const unannotated = Math.max(0, total - annotated)
      const ratio = total > 0 ? (annotated / total) : 0
      return `${(ratio * 100).toFixed(2)}% / ${(unannotated / total * 100).toFixed(2)}%`
    }
  },
  mounted() {
    const baseCanvas = this.$refs.baseCanvas
    this.baseCtx = baseCanvas.getContext('2d')
    // 关键功能：初始化总面积为画布面积（加载图片后会覆盖为图片显示区域面积）
    this.totalArea = this.canvasWidth * this.canvasHeight
    // 关键功能：默认绘制灰色背景以便观察覆盖层
    this.baseCtx.fillStyle = '#f3f4f6'
    this.baseCtx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
  },
  methods: {
    // 关键功能：加载示例图片
    loadSample() {
      const img = new Image()
      img.crossOrigin = 'anonymous'
      img.onload = () => {
        this.image = img
        this.imageLoaded = true
        this.drawImage()
      }
      // 使用公共示例图片
      img.src = 'https://images.unsplash.com/photo-1518770660439-4636190af475?q=80&w=1200&auto=format&fit=crop'
    },
    // 关键功能：加载本地图片并绘制
    onFileChange(e) {
      const file = e.target.files[0]
      if (!file) return
      if (!file.type.startsWith('image/')) {
        this.snackType = 'error'
        this.snackContent = '请选择图片文件'
        this.snackShow = true
        return
      }
      // 对齐全局上传策略：前端大小限制 5MB
      if (file.size > 5 * 1024 * 1024) {
        this.snackType = 'error'
        this.snackContent = '文件大小不能超过5MB'
        this.snackShow = true
        return
      }
      const reader = new FileReader()
      reader.onload = () => {
        const img = new Image()
        img.onload = () => {
          this.image = img
          this.imageLoaded = true
          this.drawImage()
        }
        img.src = reader.result
      }
      reader.readAsDataURL(file)
    },
    // 关键功能：按比例绘制图片到画布中心
    drawImage() {
      if (!this.image) return
      // 清空底层画布
      this.baseCtx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
      // 背景底色
      this.baseCtx.fillStyle = '#ffffff'
      this.baseCtx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
      const iw = this.image.width
      const ih = this.image.height
      const cw = this.canvasWidth
      const ch = this.canvasHeight
      const scale = Math.min(cw / iw, ch / ih)
      const w = Math.floor(iw * scale)
      const h = Math.floor(ih * scale)
      const x = Math.floor((cw - w) / 2)
      const y = Math.floor((ch - h) / 2)
      this.baseCtx.drawImage(this.image, x, y, w, h)
      // 关键功能：记录图片显示区域，重置标注与面积
      this.imageRect = { x, y, w, h }
      this.clearAnnotations()
      this.totalArea = w * h
    },
    // 关键功能：获取相对画布坐标
    getCanvasPos(e) {
      const rect = this.$refs.baseCanvas.getBoundingClientRect()
      const x = Math.round(e.clientX - rect.left)
      const y = Math.round(e.clientY - rect.top)
      return { x, y }
    },
    // 关键功能：开始新多边形
    startPolygon() {
      if (!this.imageLoaded) {
        this.snackType = 'error'
        this.snackContent = '请先加载图片再进行标注'
        this.snackShow = true
        return
      }
      this.currentPoints = []
      this.drawingPoly = true
    },
    // 关键功能：SVG 点击添加节点（限制在图片显示区域内）
    onSvgClick(e) {
      if (!this.drawingPoly) return
      const { x, y } = this.getCanvasPos(e)
      const { x: rx, y: ry, w: rw, h: rh } = this.imageRect
      if (x < rx || x > rx + rw || y < ry || y > ry + rh) {
        this.snackType = 'error'
        this.snackContent = '请在图片范围内添加标注节点'
        this.snackShow = true
        return
      }
      this.currentPoints.push({ x, y })
    },
    // 关键功能：撤销最后一个节点
    undoPoint() {
      if (!this.drawingPoly || this.currentPoints.length === 0) return
      this.currentPoints.pop()
    },
    // 关键功能：完成多边形并计算面积
    finishPolygon() {
      if (!this.drawingPoly || this.currentPoints.length < 3) return
      this.polygons.push({ points: this.currentPoints.slice() })
      this.drawingPoly = false
      this.currentPoints = []
      this.updateAnnotatedAreaVector()
    },
    // 关键功能：矢量面积计算（多边形面积累加，单位像素）
    polygonArea(points) {
      let area = 0
      const n = points.length
      for (let i = 0; i < n; i++) {
        const { x: x1, y: y1 } = points[i]
        const { x: x2, y: y2 } = points[(i + 1) % n]
        area += x1 * y2 - x2 * y1
      }
      return Math.abs(area) / 2
    },
    updateAnnotatedAreaVector() {
      let sum = 0
      for (const poly of this.polygons) {
        sum += this.polygonArea(poly.points)
      }
      this.annotatedArea = Math.round(sum)
    },
    // 关键功能：清除标注并重置统计
    clearAnnotations() {
      this.polygons = []
      this.currentPoints = []
      this.drawingPoly = false
      this.annotatedArea = 0
    }
  }
}
</script>

<style scoped>
/* 使用 Tailwind CSS，无需额外样式 */
</style>
